"""
chord.server_api

Provides the Chord server-server API
"""
### Start of imports

# This line just better work...
from jackson import debug

# There's debug stuff for the rest
debug.pre_import(__name__)

debug.importing(__name__, '* from jackson.chord.exceptions')
from jackson.chord.exceptions import *

debug.importing(__name__, 'core, db, net, sync from jackson.chord')
from jackson.chord import core, net, sync, db

debug.post_import(__name__)

### End of imports

def closest_preceding_finger_handler(key):
    """ API wrapper for core.closest_preceding_finger
    @author: ep
    @rtype: a chord.Server object
    """
    # Assert that server is active
    if not core.i_am_active():
        raise ChordServerNotActiveError
    return core.closest_preceding_finger(key)

def get_predecessor_handler():
    """ API wrapper for core.get_predecessor
    @author: ep
    @rtype: chord.Server
    """
    # Assert that server is active
    if not core.i_am_active():
        raise ChordServerNotActiveError

    return core.get_predecessor()

def get_successor_handler():
    """ API wrapper for core.get_successor
    @author: ep
    @rtype: a chord.Server object
    """
    # Assert that server is active
    if not core.i_am_active():
        raise ChordServerNotActiveError

    return core.get_successor()

def is_predecessor_handler(key):
    """ API wrapper for core.i_am_predecessor
    @author: ep
    """
    context = [__name__, 'is_predecessor_handler()']
    debug.enter(context, { 'key' : key })
    debug.comment(context, 'Checking that I am active')
    # Assert that server is active
    if not core.i_am_active():
        #print "chord.server_api.is_predecessor_handler(): i, " + str(db.get_me()) + " am not active!!!!!"
        debug.exception(context, 'I am not active, so I cannot serve requests')
        debug.leave(context, None)
        # uncommented by robert: usually with the debug module we call exception(), then leave()
        #raise ChordServerNotActiveError 

    debug.comment(context, 'Resolving query on ' + key)
    result = core.i_am_predecessor(key)
    
    debug.leave(context, { 'result' : result })
    return result

def join_handler(server):
    """ Processes a request for server to join the ring.
    @author: ep
    @param server: a chord.Server object describing the new server
    @return: { "successor" : chord.Server, "predecessor" : chord.Server }
    @todo: Add join/leave lock
    """
    context = [__name__, 'join_handler()']
    debug.enter(context, { 'joining_server' : server})
    try:
        debug.comment(context, 'Verifying that I am active')
        # Assert that server is active
        if not core.i_am_active():
            debug.exception(context, 'I am not active, so I cannot service requests')
            debug.leave(context, None)
            raise ChordServerNotActiveError    
        debug.comment(context, 'Reading the database', 3)
        # Hit the database for relevant values.
        me = db.get_me()
        my_old_pred = core.get_predecessor()
        # Check if I am the new server's proper successor.
        if core.i_am_successor(server.key):
            debug.comment(context, 'I am the successor of the joining server; preparing a welcome package')
            # If I am, prepare a response with information the new server needs.
            welcome = {}
            welcome["successor"] = me
            welcome["predecessor"] = my_old_pred

            # set my predecessor link to the new guy
            db.update_predecessor(server)

            debug.comment(context, 'tell my CDN layer it doesn\'t have to care about some keys')
            # Tell software using chord that it is no longer responsible for some keys.
            core.trigger_del_keys_event(my_old_pred.key, server.key, server)
            # Send the new server a welcome package.
            debug.leave(context, welcome)
            return welcome
        else:
            debug.comment(context, 'I am not the successor of the joining server; looking up who is')
            # If I'm not the proper successor, I should find out who is.
            succ = core.find_successor(server.key)

            debug.comment(context, 'sending a join message on behalf of joining server. Proxying join request')
            result = net.join(succ, server)
            
            debug.leave(context, result)
            # Finally, submit join request to the proper successor and relay response to new server.
            return result
    except Exception, e:
        debug.exception(context, str(e))
        debug.leave(context, None)

def leave_handler(predecessor):
    """ Handles a notification that our predecessor is leaving the ring.
    @author: ep
    @param predecessor: this server's new predecessor
    @rtype: None
    @todo: Add join/leave lock
    """
    context = [__name__, 'leave_handler()']
    debug.enter(context, { 'predecessor' : predecessor })
    try:
        debug.comment(context, 'Checking that this server is active')
        # Assert that server is active
        if not core.i_am_active():
            debug.exception(context, 'I am not active, so I cannot service requests')
            debug.leave(context, None)
            raise ChordServerNotActiveError

        # Lookup some info
        me = db.get_me()
        old_pred = core.get_predecessor()
    
        debug.comment(context, "Update my predecessor and my new predecessor's successor")
        # Update our predecessor link
        core.update_predecessor(predecessor)
        # Update our predecessor's successor link
        net.update_successor(predecessor, me)
    
        debug.comment(context, 'Inform CDN that it is getting new keys')
        # Tell software using chord that it is now responsible for some keys.
        core.trigger_add_keys_event(predecessor.key, old_pred.key, old_pred)
        
        result = True
    except Exception, e:
        debug.exception(context, str(e))
        result = False
    
    debug.leave(context, { 'result' : result })
    return result

def notify_handler(server):
    """ API wrapper to trigger a sync.notify operation.
    @deprecated: Appears to be no use for this function. Probably will be removed.
    @author: ep
    @rtype: None
    """
    # Assert that server is active
    if not core.i_am_active():
        raise ChordServerNotActiveError

    sync.notify(server)
    
def update_successor_handler(server):
    """ API wrapper for core.update_successor
    (tells the server to update its successor)
    @author: ep
    @rtype: None
    """
    context = [__name__, 'update_successor_handler()']
    debug.enter(context, { 'server_being_poked_to_update' : server })
    # Assert that server is active
    if not core.i_am_active():
        debug.exception(context, 'I am not active, so I cannot service requests')
        debug.leave(context, None)
        raise ChordServerNotActiveError
    core.update_successor(server)
    debug.leave(context, None)
